﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cmdLineParserModule;
using cmdLineParserModule.HelperClasses;

namespace cmdLineParserModule
{
    /// <summary>
    /// Parent for all classes used for marking attribute-using choices
    /// </summary>
    public abstract class optionParametrizedAttribute : optionAttribute
    {
        /// <summary>
        /// Defines if parameter for choice is required
        /// </summary>
        public bool paramRequired = true;

        /// <summary>
        /// Default value of parameter
        /// </summary>
        public object defaultValue { get; set; }

        /// <summary>
        /// Created usage text of command
        /// </summary>
        /// <param name="cmd">Command that usage text is generated for</param>
        /// <returns>Usage text in printable format</returns>
        public override string getUsageText(string cmd)
        {
            string paramInfo = TypeManager.getPureType(this.typeAccepted()).Name;

            if (!this.paramRequired)
                paramInfo = "[ " + paramInfo + " ]";

            string usageInfo = ChoiceTextManager.addChoicePrefix(cmd) + " " + paramInfo;

            if (!this.choiceRequired)
                usageInfo = "[ " + usageInfo + " ]";

            return usageInfo;
        }

        /// <summary>
        /// Checks if default value is well-defined
        /// </summary>
        public void checkDefaultValueType()
        {
            if (defaultValue == null)
                return;

            Type valueType = defaultValue.GetType();
            Type expectedType = TypeManager.getPureType(this.typeAccepted());

            if (!valueType.Equals(expectedType))
            {
                throw new WrongDefinitionException("Attribute {0} has defaultValue defined with wrong type ({1} found, {2} expected).", fieldFullName(), valueType.Name, expectedType.Name);
            }
        }
        /// <summary>
        /// Fills the field with parameter or default value or null
        /// </summary>
        /// <param name="argumentClass">Object containing the field</param>
        /// <param name="choiceToken">Token containing choice</param>
        /// <param name="parameterToken">Token containing parameter</param>
        internal override void parse(object argumentClass, cmdLineToken choiceToken, cmdLineToken parameterToken)
        {
            // value used in the end of method
            object value = null;
            
            if (choiceToken == null)
            {
                // choice is not entered

                if (this.choiceRequired)
                    throw new WrongUsageException("Choice {0} required.", this.command);
            }
            else  
            {
                // choice is entered
                
                // marking choiced as used
                choiceToken.isUsed = true;

                // parameter is not entered?
                if (parameterToken == null || parameterToken.isChoice)
                {
                    if (this.paramRequired)
                    { 
                        throw new WrongUsageException("Choice {0} requires parameter.", this.command); 
                    }
                    else
                    { 
                        value = this.defaultValue; 
                    }
                }
                else
                {
                    parameterToken.isUsed = true;
                    value = this.createValueFromString(parameterToken.originalText);
                }
            }

            this.field.SetValue(argumentClass, value);
        }

        /// <summary>
        /// Extension of custom checking
        /// </summary>
        protected override void customCheck()
        {
            base.customCheck();
            checkDefaultValueDefinition();
            checkDefaultValueType();
        }

        /// <summary>
        /// Checks if default value is well-defined (if parameter is not required, default value must be defined, and vice versa)
        /// </summary>
        private void checkDefaultValueDefinition()
        {
            if (this.paramRequired)
            {
                if (this.defaultValue != null)
                    throw new WrongDefinitionException("Attribute {0} has defaultValue specified although parameter is required (that means defaultValue is useless).", fieldFullName());
            }
            else
            {
                if (this.defaultValue == null)
                    throw new WrongDefinitionException("Attribute{0} hasn't defaultValue specified, since parameter is not required, you must specify defaultValue.", fieldFullName());
            }
        }


        /// <summary>
        /// Creates value from string, that is passed into Field when parsing.
        /// All classes derived from this must implement this method with correct type.
        /// </summary>
        /// <param name="stringValue">String value containing parameter entered by user</param>
        /// <returns>Correctly type object</returns>
        internal abstract object createValueFromString(string stringValue);

    }
}
